/*
 * PhotonRTOS础光实时操作系统 -- 锁
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#ifndef __ASM_SMP_LOCK_H
#define __ASM_SMP_LOCK_H

#include <photon/compiler.h>
#include <asm/processor.h>

#define TICKET_SHIFT	16

struct arch_smp_lock_t {
#ifdef __ARMEB__
	uint16_t next;
	uint16_t owner;
#else
	uint16_t owner;
	uint16_t next;
#endif
} __attribute__((aligned(4)));

#define __ARCH_SMP_LOCK_UNLOCKED {0, 0}


#define ARCH_FUN_arch_smp_lock_value_unlocked_DEFINED
static inline int32_t arch_smp_lock_value_unlocked(struct arch_smp_lock_t lock)
{
	return lock.owner == lock.next;
}

#define ARCH_FUN_arch_smp_lock_is_locked_p_DEFINED
static inline int32_t arch_smp_lock_is_locked(struct arch_smp_lock_t *lock)
{
	return !arch_smp_lock_value_unlocked(READ_ONCE(*lock));
}

#define ARCH_FUN_arch_smp_lock_DEFINED
static inline void arch_smp_lock(struct arch_smp_lock_t *lock)
{
	uintptr_t tmp;
	uint32_t newval;
	struct arch_smp_lock_t lockval;

	prefetchw(lock);
	asm volatile(
	"1:	ldrex	%0, [%3]\n"
	"	add	%1, %0, %4\n"
	"	strex	%2, %1, [%3]\n"
	"	teq	%2, #0\n"
	"	bne	1b"
	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
	: "r" (lock), "I" (1 << TICKET_SHIFT)
	: "cc");

	while (lockval.next != lockval.owner) {
		wfe();
		lockval.owner = ACCESS_ONCE(lock->owner);
	}

	smp_mb();

}

#define ARCH_FUN_arch_smp_trylock_DEFINED
static inline int32_t arch_smp_trylock(struct arch_smp_lock_t *lock)
{
	uintptr_t contended, res;
	uint32_t slock;

	prefetchw(lock);
	do {
		asm volatile(
		"	ldrex	%0, [%3]\n"
		"	mov	%2, #0\n"
		"	subs	%1, %0, %0, ror #16\n"
		"	addeq	%0, %0, %4\n"
		"	strexeq	%2, %0, [%3]"
		: "=&r" (slock), "=&r" (contended), "=&r" (res)
		: "r" (lock), "I" (1 << TICKET_SHIFT)
		: "cc");
	} while (res);

	if (!contended) {
		/*  */
		smp_mb();
		return 1;
	} else {
		return 0;
	}

}

#define ARCH_FUN_arch_smp_unlock_DEFINED
static inline void arch_smp_unlock(struct arch_smp_lock_t *lock)
{
	smp_mb();
	lock->owner++;
	dsb_sev();
}

#endif /* __ASM_SMP_LOCK_H */
